import weakref
import itertools

class RemotingProtocol(object):
    REQ_PING  = 1
    REQ_CALL  = 2
    REQ_QUERY = 3
    REP_VALUE = 1
    REP_ERROR = 2
    
    def __init__(self, channel, service, metaservice):
        self._channel = channel
        self._service = service
        self._metaservice = metaservice
        self._seqcounter = itertools.count()
        self._callbacks = {}
        self.call = CallingNamespace(weakref.proxy(self))
    
    def _request_async(self, callback, type, *args):
        seq = self._seqcounter.next()
        raw = brine.dump((type, seq, args))
        self._channel.send(raw)
        self._callbacks[seq] = callback
    def _request_sync(self, type, *args):
        result = [None]
        def on_ready(succ, val):
            result[0] = (succ, val)
        self._request_async(on_ready, type, *args)
        while result[0] is None:
            self.serve()
        succ, val = result[0]
        if succ:
            return val
        else:
            raise val
    
    def _dispatch(self, raw):
        type, seq, args = brine.load(raw)
        reptype, value = self._dispatchers[type](self, args)
        self._reply(reptype, seq, value)
    def _handle_ping(self, echo):
        hello = "Hello\nProtocol: %r, version: %r\nService: %r\nMeta service: %r\n"
        return True, (hello, echo)
    def _handle_call(self, args):
        funcname, pargs, kwargs = args
        kwargs = dict(kwargs)
        func = self._service[funcname]
        try:
            val = func(*pargs, **kwargs)
        except Exception, ex:
            return False, ex
        else:
            return True, ex
    def _handle_query(self, args):
        cmd, info = args
        try:
            val = self._metaservice[cmd](info)
        except Exception, ex:
            return False, ex
        else:
            return True, val
    
    def serve(self):
        raw = self._channel.recv()
        self._dispatch(raw)
    def invoke_async(self, callback, funcname, args, kwargs):
        self._request_async(self.REQ_CALL, callback, funcname, args, tuple(kwargs.iteritems()))
    def invoke_sync(self, func, args, kwargs):
        return self._request_sync(self.REQ_CALL, funcname, args, tuple(kwargs.iteritems()))
    def query_async(self, callback, cmd, *args):
        self._request_async(callback, self.REQ_CALL, cmd, args)
    def query_sync(self, callback, cmd, *args):
        return self._request_sync(self.REQ_CALL, cmd, args)
    def ping(self, echo = "it's a small world after all", timeout = 5):
        return self._request_sync(self.REQ_PING, echo)


r = RemotingProtocol(chan)
r.call.foobar(1,2,3)



